/***************************************************************************
 *   Copyright (C) 2008 by Łukasz Spintzyk   *
 *   lukasz.spintzyk@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "adconversion.h"



int8_t adc_key;             //!< Byte holds last value measured on potentiometr.
int8_t adc_avr;             //!< Byte holds last value measured on avr_adc
int8_t adc_ext;             //!< Byte holds last value measured on external ADC
uint8_t ADC_conversion=0;   //! State of ADC conversion

//private 
bool adc_key_on=false;
uint8_t adc_ch2_count=0;

/*! Function initializes Atmega internal adc
 */
void adc_init(){
    ENABLE_ADC();
    SET_VOLTAGE_REF(AVCC_V);
    SET_LEFT_ADJUST();  //set 8-bit ADC accuracy
    SET_ANALOG_CH_AND_GAIN(CH2_TB_PIN);
    ENABLE_ADC_INTERUPT();
    //! \todo   Set good prescaler value
    SET_ADC_PRESCALER(PRESCALER128);
    
    ENABLE_ADC_TRIGGER();
    SET_AUTO_TRIGGER_SRC(FREE_RUNNING);
    START_ADC();
}

void adc_stop(){
    DISABLE_ADC();
}

ISR(ADC_vect){
    
    //! \todo this function schould be writen in asm
    ADC_conversion|=(1<<ADC_DONE);
    if (GET_ANALOG_CH_AND_GAIN()==ANALOG_BUTTON_PIN){//if value from analog key
        ADC_conversion|=1<<ADC_AKEY;
        adc_key=getAVRADC();
    }
    if (GET_ANALOG_CH_AND_GAIN()==CH2_TB_PIN){      //if value from channel 2
        ADC_conversion|=1<<ADC_AVR;
        adc_avr=getAVRADC();
        int16_t variable;
        memcpy_P(&variable, &voltValue[curVoltOpt], sizeof(int16_t));
        variable*=(int8_t)(adc_avr-127);
        variable/=UDIV_H;
        variable+=ch2Pos;
        screen_memorych2[getTime()]=variable;
    }
    if (adc_key_on){                                //if read analog value key mode 
        adc_ch2_count++; 
        if (adc_ch2_count==20){                     //we need only 5/25 samples
            SET_ANALOG_CH_AND_GAIN(ANALOG_BUTTON_PIN);
        }
        else if (adc_ch2_count==25){                //rest of 20/25 samples goes to ch2
            SET_ANALOG_CH_AND_GAIN(CH2_TB_PIN);
            adc_ch2_count=0;
        }
    }
    
}

uint8_t getExtADCValue(){
    uint8_t temp=DDR(ADCPORT_ext);
    uint8_t temp2=PORT(ADCPORT_ext);
    DDR(ADCPORT_ext)=0x00;  //set port as input   
    PORT(ADCPORT_ext)=0xFF; //set pull-up resistors
    
    PORT(LCD_CONTROL)&=(uint8_t)~(1<<ADC_CS);
    //wait 600ns -> about 10 instructions
    uint8_t i=0;
    for (i=0;i<10;i++);
    i=PIN(ADCPORT_ext);
    
    PORT(ADCPORT_ext)=temp2;
    DDR(ADCPORT_ext)=temp;
    return i;
    
}
/*
SIGNAL(TIMER1_OVF_vect){
//! \todo Narazie ta funkcja nie obs�uguje kana��w add i sub
//! \todo T� funkcj� najlepiej to potem w assemblerze napisa�
        //odczyta� z ADC028
    PORT(CONTROL)&=(~(1<<ADC_CS));//sygna�e cs, wr rd na zero po 100-150 ns a potem na liniach warto��
    asm ("nop");
    asm ("nop");
    adc_ext=(-1)*((int8_t)PIN(DATA));   // Poniewa� warto� od 128-255 jest dodatnia, a 0-127 ujemna
    PORT(CONTROL)|=(1<<ADC_CS);
    ADC_conversion|=1<<ADC_EXT;
    pierwszy.sumVal+=adc_ext;
    if (++pierwszy.sampleCount==sampleNum[curTimeOpt]) {
        pierwszy.val=pierwszy.sumVal/pierwszy.sampleCount+ch1Pos;// dodaj przesuni�cie na ekranie -> mamy pixel do zapalenia na wy�wietlaczu
        sendToDisplay(pierwszy.val);

        time++;
    }
}
*/

inline void SET_VOLTAGE_REF(uint8_t x) {	//x=0 - 3
    ADMUX&=0x3F;
    ADMUX|=x<<6;
}

inline void SET_ANALOG_CH_AND_GAIN(uint8_t x){

    x&=0x1F;	//clear 7,6,5 bit;
    ADMUX&=0xE0;	//clear bits MUX4:0
    ADMUX|=x;
}

inline uint8_t GET_ANALOG_CH_AND_GAIN(){
    uint8_t x=0x1F&ADMUX;
    return x;
}

inline void SET_ADC_PRESCALER(uint8_t x){
    x&=~0xF8;
    ADCSRA&=0xF8;
    ADCSRA|=x;
}

inline void SET_AUTO_TRIGGER_SRC(uint8_t x){
    ADMUX&=0x3F;
    x&=0xC0;
    ADMUX|=x;
}

inline int8_t getAVRADC(){
  /* Universal but slow
      int8_t temp;
    if (GET_ADJUST()) {
        //left adjust
                //temp=ADCH*256/Voltages[GET_VOLTAGE_REF()];	
        temp=ADCH*REFERENCE_VOLTAGE/256;
    } else {
        //right adjust
        temp=(int8_t)((((ADCH&0x03)<<8) + ADCL)*REFERENCE_VOLTAGE/1024);
    }
    return (int8_t)temp;
   */
    //assuming that is left adjust, and ref voltage is REFERENCE_VOLTAGET
    return ADCH;//*REFERENCE_VOLTAGE/0xFF;
}

void runADC_AKey(bool on){
    adc_key_on=on;
}

